
#include "ControladorCliente.h"

ControladorCliente::ControladorCliente(int cliente, int tipoPersonaje, int pisoNivel, int anchoNivel, int altoNivel, int gravedad, EstadoJuego estado) {
	
	string log = "cliente_" + GestionTiposPersonaje::getListaTipos().at(tipoPersonaje) + ".log";
	log_controladorCliente.open(log.c_str());
	log_controladorCliente << "Abierto log cliente\n" << flush;
	
	this->socket = cliente;
	
	pthread_mutex_init(&mutexEstadoJuego, NULL);
	pthread_mutex_init(&mutexEstadoConexion, NULL);
	
	srand(time(NULL));
	Punto posJugador(rand() % anchoNivel, 0);

	this->escritura = new ControladorEscrituraCliente(posJugador);
	this->lectura = new ControladorLecturaCliente(posJugador, tipoPersonaje, pisoNivel, anchoNivel, altoNivel,gravedad);
	
	this->estadoJuego = estado;
	this->estadoConexion = Activa;

}

ControladorCliente::~ControladorCliente() {
	
	log_controladorCliente << "Voy a eliminar\n" << flush;
	delete lectura;
	delete escritura;
	
	pthread_mutex_destroy(&mutexEstadoJuego);
	pthread_mutex_destroy(&mutexEstadoConexion);
	
	log_controladorCliente << "Finalizado cliente\n" << flush;
	log_controladorCliente.close();
}

void ControladorCliente::ManejarCliente() {

	pthread_t threadEscribe;
	pthread_t threadLee;
	
	if (pthread_create (&threadEscribe, NULL, ServidorEnvia, this) != 0) {
			cout << "No puedo crear thread";
			exit (1);
	}
	
	if (pthread_create (&threadLee, NULL, ServidorRecibe, this) != 0) {
		cout << "No puedo crear thread";
		exit (1);
	}
}

void ControladorCliente::setEstadoConexion(EstadoConexion estado) {
	
	pthread_mutex_lock(&mutexEstadoConexion);
	if (this->estadoConexion != estado) {
		this->estadoConexion = estado;
		log_controladorCliente << "Cambio estado de conexion a " << estado << endl << flush;
	}
	pthread_mutex_unlock(&mutexEstadoConexion);

}

void ControladorCliente::setEstadoJuego(EstadoJuego estado) {
	
	pthread_mutex_lock(&mutexEstadoJuego);
	if (this->estadoJuego != estado) {
		this->estadoJuego = estado;
		log_controladorCliente << "Cambio estado de juego a " << estado << endl << flush;
	}
	pthread_mutex_unlock(&mutexEstadoJuego);
}

EstadoConexion ControladorCliente::getEstadoConexion() {
	
	pthread_mutex_lock(&mutexEstadoConexion);
	EstadoConexion e = estadoConexion;
	pthread_mutex_unlock(&mutexEstadoConexion);
	
	return e;
}

EstadoJuego ControladorCliente::getEstadoJuego() {
	
	pthread_mutex_lock(&mutexEstadoJuego);
	EstadoJuego e = estadoJuego;
	pthread_mutex_unlock(&mutexEstadoJuego);
	return e;
}

int ControladorCliente::getSocket() {
	
	return socket;
}

ControladorLecturaCliente* ControladorCliente::getControladorLectura() {
	
	return lectura;
}

ControladorEscrituraCliente* ControladorCliente::getControladorEscritura() {
	
	return escritura;
}

void ControladorCliente::CongelarPosicionJugador() {
	
	getControladorEscritura()->setPosicionJugador(getControladorLectura()->getControlador()->getJugador()->getPosicion());
}

void ControladorCliente::EnviarMensaje(string update, string tipo) {
	
	string mensaje = tipo+"#<" + update + ">";
	int res = writeSocket(getSocket(), mensaje);
	if (res == SIN_CONEXION)
		setEstadoConexion(Inactiva);
}

// ********************************************************************
// Funciones de threads
// ********************************************************************

void* ServidorEnvia(void* data) {
	
	ControladorCliente* controladorCliente = (ControladorCliente*) data;
	string ultimoMensajeDibujables = "";
	string ultimoMensajePuntaje = "";
	string ultimoMensajeSonidos = "";
	
	Timer timer;
	timer.start();
	
	Uint32 tiempoUltimoMensaje;		
	
	controladorCliente->log_controladorCliente << "Comenzando envio de mensajes\n" << flush;
	while (true) {
		
		SDL_Delay(DELAY_CABEZA_SERVER);

		if (controladorCliente->getControladorLectura()->getControlador()->getJugador()->getEstadoVida() == Muerto) {
			controladorCliente->getControladorEscritura()->setEstadoEscritura(EscrituraFin);
			controladorCliente->log_controladorCliente << "Seteando escritura en fin\n" << flush;
			break;
		}
		
		if (controladorCliente->getEstadoConexion() == Inactiva) {
			controladorCliente->getControladorEscritura()->setEstadoEscritura(EscrituraInactiva);
			controladorCliente->log_controladorCliente << "Seteando escritura en inactiva\n" << flush;
			break;
		}
		
		if (controladorCliente->getEstadoJuego() == FinForzado) {
			controladorCliente->getControladorEscritura()->setEstadoEscritura(EscrituraFin);
			controladorCliente->log_controladorCliente << "Seteando escritura en fin\n" << flush;
			break;
		}
		
		string mensaje = "";
		vector<string> updates = controladorCliente->getControladorEscritura()->getUpdates();
		for (unsigned int i=0; i< updates.size(); i++)
			mensaje += "U#<" + updates.at(i) + ">";
			
		if (mensaje.size() > 0) {
			
			int res = writeSocket(controladorCliente->getSocket(), mensaje);

			tiempoUltimoMensaje = timer.getTick();
			if (res == SIN_CONEXION) {
				controladorCliente->setEstadoConexion(Inactiva);
				continue;
			}
		}
		
		string puntajes = controladorCliente->getControladorEscritura()->getPuntajes();

		if (puntajes != ultimoMensajePuntaje) { //cambio

			ultimoMensajePuntaje = puntajes;
			puntajes = "P#" + puntajes;
			int res = writeSocket(controladorCliente->getSocket(), puntajes);

			tiempoUltimoMensaje = timer.getTick();
			if (res == SIN_CONEXION) {
				controladorCliente->setEstadoConexion(Inactiva);
				continue;
			}

		}
		
		if (controladorCliente->getControladorLectura()->getEstadoLectura() == LecturaCargando) continue;
		
		if (controladorCliente->getEstadoJuego() == Jugando) {
			
			Uint32 tiempo = timer.getTick();
			
			mensaje = "D#" + controladorCliente->getControladorEscritura()->getDibujables();
			
			if (mensaje != ultimoMensajeDibujables || tiempo - tiempoUltimoMensaje >= TIEMPO_ENTRE_MENSAJES_SERVER ) {
				ultimoMensajeDibujables = mensaje;
				
				int res = writeSocket(controladorCliente->getSocket(), mensaje);
				tiempoUltimoMensaje = timer.getTick();
				
				if (res == SIN_CONEXION) {
					controladorCliente->setEstadoConexion(Inactiva);
					continue;
				}
			}
			
			mensaje = "S#" + controladorCliente->getControladorEscritura()->getSonidos();
			
			if (mensaje != ultimoMensajeSonidos || tiempo - tiempoUltimoMensaje >= TIEMPO_ENTRE_MENSAJES_SERVER ) {
				ultimoMensajeSonidos = mensaje;
				
				int res = writeSocket(controladorCliente->getSocket(), mensaje);
				tiempoUltimoMensaje = timer.getTick();

				if (res == SIN_CONEXION) {
					controladorCliente->setEstadoConexion(Inactiva);
					continue;
				}
			}
		}
				
		if (controladorCliente->getEstadoJuego() == FinJuego) {
			
			string mensaje = "";
			// mensaje = serializar "FIN JUEGO";
			int res = writeSocket(controladorCliente->getSocket(), mensaje);
			
			if (res == SIN_CONEXION) {
				controladorCliente->setEstadoConexion(Inactiva);
				continue;
			}

			controladorCliente->getControladorEscritura()->setEstadoEscritura(EscrituraFin);
			controladorCliente->log_controladorCliente << "Seteando escritura en fin\n" << flush;			
			break;
		}
	}

	return NULL;
}

void* ServidorRecibe(void* data) {
	
	ControladorCliente* controladorCliente = (ControladorCliente*) data;
	int largoMensaje = 0;
	
	while (controladorCliente->getControladorLectura()->getEstadoLectura() == LecturaCargando) {
		usleep(3000);
	}
		
	Timer timer;
	timer.start();
	Uint32 lastMessageTime = timer.getTick();
	
	controladorCliente->log_controladorCliente << "Comenzando recibo de mensajes\n" << flush;
	while (true) {
		
		SDL_Delay(DELAY_CABEZA_SERVER);

		if (controladorCliente->getControladorEscritura()->getEstadoEscritura() == EscrituraInactiva) {
			controladorCliente->getControladorLectura()->setEstadoLectura(LecturaInactiva);
			controladorCliente->log_controladorCliente << "Seteando lectura en inactiva\n" << flush;
			break;
		}
		
		if (controladorCliente->getControladorEscritura()->getEstadoEscritura() == EscrituraFin) {
			controladorCliente->getControladorLectura()->setEstadoLectura(LecturaFin);
			controladorCliente->log_controladorCliente << "Seteando lectura en fin\n" << flush;
			break;
		}
		
		if (controladorCliente->getEstadoConexion() == Inactiva || controladorCliente->getEstadoJuego() == FinForzado) {
			continue;
		}
		
		string mensaje = "";
		ioctl(controladorCliente->getSocket(), FIONREAD, &largoMensaje);
		if (largoMensaje > 0) {
			int res = readSocket(controladorCliente->getSocket(), &mensaje, largoMensaje);
			lastMessageTime = timer.getTick();
			if (res == SIN_CONEXION) {
				controladorCliente->setEstadoConexion(Inactiva);
				continue;
			}
		}
		else {
			if ( timer.getTick() - lastMessageTime > SERVER_TIMEOUT ) {
				controladorCliente->setEstadoConexion(Inactiva);
				controladorCliente->log_controladorCliente << "Cerrando por timeout" << endl << flush;
				continue;
			}
		}
		
		controladorCliente->getControladorLectura()->getControlador()->DecodificarTeclas(mensaje);
		controladorCliente->getControladorLectura()->LockJugador();
		controladorCliente->getControladorLectura()->getControlador()->manejarInput();
		controladorCliente->getControladorLectura()->UnlockJugador();
	}

	return NULL;
}
